/******************************************************************************
 * Copyright (C) 2017 Chengdu Dlhd information industry co. LTD.
 * All Rights Reserved.
 * 本软件为成都顶联互动信息产业有限公司开发研制。未经本公司正式书面同意，其他任何个人、团体不得使用、复制、修改或发布本软件.
 *****************************************************************************/
package net.dgg.dggcloud.service.iboss.service.sc;/**
 * Created by Administrator on 2017/12/5.
 */

import net.dgg.dggcloud.service.common.base.SessionManager;
import net.dgg.dggcloud.service.common.base.service.BaseService;
import net.dgg.dggcloud.service.entity.org.OrganizationEntity;
import net.dgg.dggcloud.service.entity.user.User;
import net.dgg.dggcloud.service.iboss.constant.OverdueStateConstant;
import net.dgg.dggcloud.service.iboss.dao.sc.ScProOrderDao;
import net.dgg.dggcloud.service.iboss.dao.sc.ScProOrderTimeStateDao;
import net.dgg.dggcloud.service.iboss.entity.ScProFlowNode;
import net.dgg.dggcloud.service.iboss.entity.ScProOrder;
import net.dgg.dggcloud.service.iboss.entity.ScProOrderTimeState;
import net.dgg.dggcloud.service.jrcrm.service.cms.WorkDayService;
import net.dgg.dggcloud.service.service.organization.OrganizationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author 刘阳
 * @ClassName <ScProOrderTimeStateService>
 * @despriction 生产订单超期状态实体
 * @create 2017/12/05 11:40
 **/
@Service
public class ScProOrderTimeStateService extends BaseService {
    @Autowired
    private ScProOrderTimeStateDao scProOrderTimeStateDao;
    @Autowired
    private ScProOrderDao scProOrderDao;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private NodeManagerService nodeManagerService;
    @Autowired
    private WorkDayService workDayService;
    @Autowired
    private AllProOrderService allProOrderService;


    /**
     * 创建状态数据
     *
     * @param scProOrder
     */
    public Map addScProOrderTimeState(ScProOrder scProOrder) {
        Assert.notNull(scProOrder, "生产子订单不能为空！");
        User user = SessionManager.getCurrentUser();
        Assert.notNull(user, "当前用户获取失败，请刷新重新登录！");
        OrganizationEntity org = user.getOrgId() == null ? null : this.repository.get(OrganizationEntity.class, user.getOrgId());

        //查询节点流程
        Long bigOrgId = this.organizationService.getOrganizationDepartmentId(user.getOrgId()); //事业部ID
        OrganizationEntity bigOrg = this.repository.get(OrganizationEntity.class, bigOrgId);
        Assert.notNull(bigOrg, "事业部查询结果为空，请检查自己部门信息！");
        Assert.hasText(scProOrder.getBtCode(), String.format("编号为%s的订单的业态编码为空,请联系管理员！", scProOrder.getOrderBusinessNo()));
        Map conMap = new HashMap();
        conMap.put("deptids", String.valueOf(bigOrgId));
        conMap.put("businessCode", scProOrder.getBtCode());
        List<Map> mapList = this.nodeManagerService.listNodemanagerByMap(conMap);
        Assert.notNull(mapList, "节点流程查询失败！");
        Assert.isTrue(mapList.size() != 0, "未查询到本部门所属业态的节点流程，请在节点管理页面添加！");
        Assert.isTrue(mapList.size() == 1, "查询到本部门所属业态的多个节点流程！");
        Map nodeFlowMap = mapList.get(0);
        String managementCycleKey = "managementcycle";
        Integer managementCycle = nodeFlowMap.get(managementCycleKey) != null ? Integer.valueOf(String.valueOf(nodeFlowMap.get(managementCycleKey))) : null;
        Assert.notNull(managementCycle, "本部门所属业态的节点流程办理周期为空！");
        Long nodeFlowId = nodeFlowMap.containsKey("id") ? Long.valueOf(String.valueOf(nodeFlowMap.get("id"))) : null;
        Assert.notNull(nodeFlowId, "本部门所属业态的节点流程Id为空！");

        // 查询首节点
        Map conMap2 = new HashMap();
        conMap2.put("processid", String.valueOf(nodeFlowId));
        conMap2.put("orderBySort", "sort");
        List<Map> firstNodeMapList = this.nodeManagerService.queryNodeList(conMap2);
        Assert.notNull(firstNodeMapList, "节点查询失败！");
        Assert.isTrue(firstNodeMapList.size() != 0, "未查询到本部门所属业态的流程节点，请在节点管理页面添加！");
        Map firstNodeMap = firstNodeMapList.get(0);
        Long firstNodeId = firstNodeMap.containsKey("id") ? Long.valueOf(String.valueOf(firstNodeMap.get("id"))) : null;
        Assert.notNull(firstNodeId, "首节点获取失败！");
        String handledatekey = "handledate";
        Integer handledate = firstNodeMap.get(handledatekey) != null ? Integer.valueOf(String.valueOf(firstNodeMap.get(handledatekey))) : null;
        Assert.notNull(handledate, String.format("流程编号为%s的%s节点办理周期获取失败，请检查配置！", String.valueOf(nodeFlowMap.get("flowno")), String.valueOf(firstNodeMap.get("name"))));

        scProOrder.setNodeFlowId(nodeFlowId); // 节点流程ID
        scProOrder.setProductUltraTime(OverdueStateConstant.NORMAL); //订单超期状态初始化
        scProOrder.setCurNodeId(firstNodeId); //首节点
        scProOrder.setNodeUltraTime(OverdueStateConstant.NORMAL); // 节点超期状态初始化
        scProOrder.setCurtNode(String.valueOf(firstNodeMap.containsKey("name") ? String.valueOf(firstNodeMap.get("name")) : null));
        if (scProOrder.getId() == null) {
            scProOrder.setId(this.keyWorker.nextId());
        }

        //状态实体
        ScProOrderTimeState state = this.repository.get(ScProOrderTimeState.class, scProOrder.getId());
        if (state == null) {
            state = new ScProOrderTimeState();
        }
        state.setId(scProOrder.getId()); // 生产子订单id
        state.setScProOrderNo(scProOrder.getOrderBusinessNo());  //生产子订单编号
        state.setOrderSuspendTime(0L); //暂停总时间时间（毫秒）
        state.setOrderProcessDay(managementCycle);  //订单办理天数（天）
        Date startDate = this.getStartDate(new Date()); //获取计时开始时间
        state.setOrderStartDateTime(startDate); // 订单计时开始时间
        state.setOrderOverdueTime(this.getWorkDayFrom(startDate, managementCycle, state.getOrderSuspendTime())); // 订单预计超期时间
        state.setOrderOverdueState(OverdueStateConstant.NORMAL); //订单超期状态
        //节点状态数据
        state.setNodeProcessDay(handledate); //节点办理天数（天）
        state.setNodeStartDateTime(startDate); // 节点计时开始时间
        state.setNodeSuspendTime(0L); //暂停总时间时间（毫秒）
        state.setNodeOverdueTime(this.getWorkDayFrom(startDate, handledate, state.getNodeSuspendTime())); // nodeOverdueTime
        state.setNodeOverdueState(OverdueStateConstant.NORMAL);//节点超期状态
        state.setFlag(1); //需要被更新
        state.setId(scProOrder.getId()); // ID 一致

        this.repository.saveOrUpdate(scProOrder);
        this.repository.saveOrUpdate(state);

        Map map = new HashMap();
        map.put("scorderid", scProOrder.getId());
        map.put("completenodeid", firstNodeId);
        map.put("completenodename", firstNodeMap.get("name"));
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        map.put("nextFollowTime", fmt.format(state.getNodeOverdueTime())); // 接单的跟进时间

        return map;
    }

    /**
     * 获取从包括接收日期开始的第一个的工作日
     *
     * @param date
     * @return
     */
    private Date getStartDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        Date firstDay = this.getWorkDayFrom(calendar.getTime(), 0); // 获取从昨日开始的第一个工作日
        Calendar calendarFirst = Calendar.getInstance();
        calendarFirst.setTime(firstDay);
        //判断当前是否是工作日，否则以第一个工作日的零点开始计时
        if (calendar.get(Calendar.DAY_OF_YEAR) == calendarFirst.get(Calendar.DAY_OF_YEAR)) {
            return calendar.getTime();
        } else {
            calendarFirst.set(Calendar.HOUR_OF_DAY, 0);
            calendarFirst.set(Calendar.MINUTE, 0);
            calendarFirst.set(Calendar.SECOND, 0);
            calendarFirst.set(Calendar.MILLISECOND, 0);
            return calendarFirst.getTime();
        }
    }


    /**
     * 跟进进度时 调用此方法修改生产子订单超时状态
     *
     * @param scProOrder
     */
    public void updateTimeStatusByScProOrder(ScProOrder scProOrder) {
        Assert.notNull(scProOrder, "生产订单不能为空！");
        Assert.notNull(scProOrder.getId(), "生产订单ID不能为空！");
        Assert.notNull(scProOrder.getCurNodeId(), String.format("编号为%s的生产子订单当前节点ID为空!", scProOrder.getOrderBusinessNo()));
        ScProFlowNode node = this.repository.get(ScProFlowNode.class, scProOrder.getCurNodeId());
        Assert.notNull(node, String.format("编号为%s的生产子订单未查询到节点数据!", scProOrder.getOrderBusinessNo()));
        Assert.notNull(node.getHandledate(), String.format("名字为%s的节点办理周期为空!", node.getName()));
        Assert.notNull(scProOrder.getNextFollowTime(), "下次跟进时间不能为空！");

        ScProOrderTimeState state = this.repository.get(ScProOrderTimeState.class, scProOrder.getId());
        Assert.notNull(state, String.format("编号为%s的生产子订单超期数据为空!", scProOrder.getOrderBusinessNo()));

        Date receiveDate = this.getStartDate(new Date());
        state.setNodeStartDateTime(receiveDate); // 节点计时 时间
        state.setNodeProcessDay(node.getHandledate());
        state.setNodeSuspendTime(0L); // 暂停时间归零
        state.setNodeOverdueTime(this.getWorkDayFrom(receiveDate, state.getNodeProcessDay(), 0L)); // 预计超期时间
        state.setNodeOverdueState(OverdueStateConstant.NORMAL);
        state.setNextFollowTime(scProOrder.getNextFollowTime()); // 下次跟进时间
        state.setFollowOverdueState(OverdueStateConstant.NORMAL); // 重置
        this.repository.saveOrUpdate(state);
    }


    /**
     * 暂停 超时状态自动更新
     *
     * @param scProOrderId
     */
    public void update_suspendScProOrderTime(Long scProOrderId) {
        Assert.notNull(scProOrderId, "生产子订单ID不能为空!");
        ScProOrderTimeState state = this.repository.get(ScProOrderTimeState.class, scProOrderId);
        Assert.notNull(state, "未查询订单超期数据！");
        state.setFlag(0); // 标识
        this.repository.saveOrUpdate(state);
    }

    /**
     * 恢复 超时状态自动更新
     *
     * @param scProOrderId 生产子订单
     * @param suspendDate  暂停开始时间
     * @param recoverDate  恢复时间
     */
    public void update_recoverScProOrderTime(Long scProOrderId, Date suspendDate, Date recoverDate) {
        Assert.notNull(scProOrderId, "生产子订单ID不能为空!");
        Assert.notNull(suspendDate, "暂停时间不能为空!");
        Assert.notNull(recoverDate, "回复时间不能为空!");
        Assert.isTrue(recoverDate.getTime() > suspendDate.getTime(), "回复时间不能小于暂停时间!");
        ScProOrderTimeState state = this.repository.get(ScProOrderTimeState.class, scProOrderId);
        Assert.notNull(state, "未查询订单超期数据！");
        Long suspendTime = this.getSuspendAllTime(suspendDate, recoverDate);
        Date curDate = new Date();
        state.setNodeSuspendTime(state.getNodeSuspendTime() + suspendTime);  //以下更新节点超期信息
        state.setNodeOverdueTime(this.getWorkDayFrom(state.getNodeStartDateTime(), state.getNodeProcessDay(), state.getNodeSuspendTime()));
        state.setNodeOverdueState(curDate.getTime() <= state.getNodeOverdueTime().getTime() ? OverdueStateConstant.NORMAL : OverdueStateConstant.TIME_OUT);
        state.setOrderSuspendTime(state.getOrderSuspendTime() + suspendTime); //更新订单超期信息
        state.setOrderOverdueTime(this.getWorkDayFrom(state.getOrderStartDateTime(), state.getOrderProcessDay(), state.getOrderSuspendTime()));
        state.setOrderOverdueState(curDate.getTime() <= state.getOrderOverdueTime().getTime() ? OverdueStateConstant.NORMAL : OverdueStateConstant.TIME_OUT);
        state.setFlag(1); // 恢复 状态更新
        this.repository.saveOrUpdate(state);
    }

    /**
     * test
     */
    private void testGetSuspendAllTime() {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            Date startDate = fmt.parse("2017-12-07 14:31:14");
            Date endDate = fmt.parse("2017-12-10 14:31:14");
            Long time = this.getSuspendAllTime(startDate, endDate);
            Long d = time / (24 * 3600 * 1000), h = (time % (24 * 3600 * 1000)) / (3600 * 1000), m = (time % (3600 * 1000)) / (60 * 1000), s = (time % (60 * 1000)) / 1000, ms = time % 1000;
            System.out.println(String.format("%s天%S时%s分%s秒%s毫秒", d, h, m, s, ms));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Assert.isTrue(false, "test!");
    }


    /**
     * 得到暂停时间段里面 与工作日时间的交集 毫秒
     *
     * @param suspendDate
     * @param recoverDate
     * @return
     */
    private Long getSuspendAllTime(Date suspendDate, Date recoverDate) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        Map map = new HashMap();
        map.put("startDate", fmt.format(suspendDate));
        map.put("endDate", fmt.format(recoverDate));
        List<Map> workDaysInSus = this.workDayService.listWorkDaysbetweenDays(map);
        Assert.notNull(workDaysInSus, "查询工作日失败！");
        Set<String> stringSet = new HashSet<>();
        for (Map w : workDaysInSus) {
            stringSet.add(fmt.format(w.get("date")));
        }

        Calendar calendarStart = Calendar.getInstance(), calendarEnd = Calendar.getInstance();
        calendarStart.setTime(suspendDate);
        calendarEnd.setTime(recoverDate);
        Long all = 0L;
        String suspendDateStr = fmt.format(suspendDate);
        if (stringSet.contains(suspendDateStr)) { //开始暂停时间是工作日
            all += 24 * 3600 * 1000 - (calendarStart.get(Calendar.HOUR_OF_DAY) * 3600 * 1000 + calendarStart.get(Calendar.MINUTE) * 60 * 1000 + calendarStart.get(Calendar.SECOND) * 1000 + calendarStart.get(Calendar.MILLISECOND));
            stringSet.remove(suspendDateStr);
        }
        String recoverDateStr = fmt.format(recoverDate);
        if (stringSet.contains(recoverDateStr)) { // 恢复时间是工作日
            all += calendarEnd.get(Calendar.HOUR_OF_DAY) * 3600 * 1000 + calendarEnd.get(Calendar.MINUTE) * 60 * 1000 + calendarEnd.get(Calendar.SECOND) * 1000 + calendarEnd.get(Calendar.MILLISECOND);
            stringSet.remove(recoverDateStr);
        }
        all += stringSet.size() * 24 * 3600 * 1000L;
        return all;
    }


    /**
     * 获取代入暂停时间后的 从某日开始x个工作日的日期
     *
     * @param start          计时开始时间
     * @param len            办理时间
     * @param suspendMiniSec 暂停毫秒数字
     * @return
     */
    private Date getWorkDayFrom(Date start, Integer len, Long suspendMiniSec) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(start);
        int h = calendar.get(Calendar.HOUR_OF_DAY), m = calendar.get(Calendar.MINUTE), s = calendar.get(Calendar.SECOND), ms = calendar.get(Calendar.MILLISECOND);
        Long all = h * 3600 * 1000L + m * 60 * 1000L + s * 1000L + len * 24 * 3600 * 1000L + suspendMiniSec, dayMs = 24 * 3600 * 1000L;
        Long ext = all % dayMs, days = all / dayMs;
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        Map map = new HashMap();
        map.put("date", fmt.format(start));
        map.put("days", String.valueOf(days));
        String dateStr = this.workDayService.queryAfterDate(map);
        Assert.hasText(dateStr, "获取工作日失败！");
        Date date = null;
        try {
            date = fmt.parse(dateStr);
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(date);
            calendar1.add(Calendar.MILLISECOND, ext.intValue());
            return calendar1.getTime();
        } catch (ParseException e) {
            Assert.isTrue(false, "工作日期转换出现异常！");
        }
        return date;

    }

    /**
     * 获取从某日开始x个工作日的日期
     *
     * @param start
     * @param len
     * @return
     */
    private Date getWorkDayFrom(Date start, Integer len) {
        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        Calendar calendarStart = Calendar.getInstance();
        calendarStart.setTime(start);
        Map map = new HashMap();
        map.put("date", fmt.format(start));
        map.put("days", String.valueOf(len));
        String dateStr = this.workDayService.queryAfterDate(map);
        Assert.hasText(dateStr, "获取工作日失败！");
        Date date = null;
        try {
            date = fmt.parse(dateStr);
            Calendar calendar1 = Calendar.getInstance();
            calendar1.setTime(date);
            calendar1.set(Calendar.HOUR_OF_DAY, calendarStart.get(Calendar.HOUR_OF_DAY));
            calendar1.set(Calendar.MINUTE, calendarStart.get(Calendar.MINUTE));
            calendar1.set(Calendar.SECOND, calendarStart.get(Calendar.SECOND));
            calendar1.set(Calendar.MILLISECOND, calendarStart.get(Calendar.MILLISECOND));
            return calendar1.getTime();
        } catch (ParseException e) {
            Assert.isTrue(false, "工作日期转换出现异常！");
        }
        return date;
    }


}


